#define UTF_INVALID 0xFFFD
int utf8decode(const char *s_in, long *u, int *err)
{
    const unsigned char *s = (const unsigned char *)s_in;
    *u = UTF_INVALID; // Default to replacement character
    *err = 1;         // Assume error initially

    // Handle empty input
    if (*s == '\0')
        return 0;

    // 1-byte ASCII (0xxxxxxx)
    if ((s[0] & 0x80) == 0)
    {
        *u = s[0];
        *err = 0;
        return 1;
    }

    // 2-byte sequence (110xxxxx 10xxxxxx)
    if ((s[0] & 0xE0) == 0xC0)
    {
        if (s[1] == '\0' || (s[1] & 0xC0) != 0x80)
            return 1; // Invalid second byte
        *u = ((s[0] & 0x1F) << 6) | (s[1] & 0x3F);
        if (*u < 0x80)
            return 1; // Overlong encoding
        *err = 0;
        return 2;
    }

    // 3-byte sequence (1110xxxx 10xxxxxx 10xxxxxx) → Covers U+276F (❯)
    if ((s[0] & 0xF0) == 0xE0)
    {
        if (s[1] == '\0' || (s[1] & 0xC0) != 0x80)
            return 1;
        if (s[2] == '\0' || (s[2] & 0xC0) != 0x80)
            return 2;
        *u = ((s[0] & 0x0F) << 12) | ((s[1] & 0x3F) << 6) | (s[2] & 0x3F);
        if (*u < 0x800)
            return 1; // Overlong encoding
        if ((*u >= 0xD800) && (*u <= 0xDFFF))
            return 3; // Surrogate pair (invalid)
        *err = 0;
        return 3;
    }

    // 4-byte sequence (11110xxx 10xxxxxx 10xxxxxx 10xxxxxx)
    if ((s[0] & 0xF8) == 0xF0)
    {
        if (s[1] == '\0' || (s[1] & 0xC0) != 0x80)
            return 1;
        if (s[2] == '\0' || (s[2] & 0xC0) != 0x80)
            return 2;
        if (s[3] == '\0' || (s[3] & 0xC0) != 0x80)
            return 3;
        *u = ((s[0] & 0x07) << 18) | ((s[1] & 0x3F) << 12) | ((s[2] & 0x3F) << 6) | (s[3] & 0x3F);
        if (*u < 0x10000 || *u > 0x10FFFF)
            return 1; // Out of Unicode range
        *err = 0;
        return 4;
    }

    // Invalid UTF-8 leading byte
    return 1;
}
